// Copyright (C) 2013-2016 DNAnexus, Inc.
//
// This file is part of dx-toolkit (DNAnexus platform client libraries).
//
//   Licensed under the Apache License, Version 2.0 (the "License"); you may
//   not use this file except in compliance with the License. You may obtain a
//   copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
//   WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
//   License for the specific language governing permissions and limitations
//   under the License.

/** \file
 *
 * \brief Exception classes.
 */

#ifndef DXCPP_EXCEPTIONS_H
#define DXCPP_EXCEPTIONS_H

#include <exception>
#include <string>
#include <boost/lexical_cast.hpp>

namespace dx {
  /**
   * Contains some common error types thrown by DXError and derived classes
   */
  namespace DXErrorTypes {
    static const std::string DEFAULT_ERROR = "DXDefaultError";
    static const std::string DXFILE_ERROR = "DXFileError";
    static const std::string DXAPP_ERROR = "DXAppError";
    static const std::string DXJOB_ERROR = "DXJobError";
    static const std::string DXCONNECTION_ERROR = "DXConnectionError";
    static const std::string DXNOT_IMPLEMENTED_ERROR = "DXNotImplementedError"; 
  }

  //! Generic exception for the DNAnexus C++ library.
  class DXError: public std::exception {
  public:
    std::string msg;  //!< The actual error message
    std::string type; //!< Type of the error message (can be a free-form string, but usually one of the static members from DXError class)
    mutable std::string error_msg; // store the error message generated by what()

    DXError(): msg("Unknown error occured while using dxcpp.") { }
    DXError(const std::string &msg, const std::string &type = DXErrorTypes::DEFAULT_ERROR): msg(msg), type(type) { }
    virtual const char* what() const throw() {
      error_msg = type + ": '" + msg + "'";
      return (const char*)msg.c_str();
    }
  
    virtual ~DXError() throw() { }

  };
  
  //! Represents errors returned by the API server.
  ///
  /// This exception is thrown when a request made to the API server results in
  /// an HTTP response code other than 200.
  class DXAPIError: public DXError {
  public:
    int resp_code; //!< HTTP status code returned by the apiserver 

    DXAPIError(std::string msg, std::string type, int resp_code) :
    DXError(msg, type), resp_code(resp_code) {}

    virtual const char* what() const throw() {
      error_msg = type + ": '" + msg + "', Server returned HTTP code '" + boost::lexical_cast<std::string>(resp_code) + "'";
      return (const char*)error_msg.c_str();
    }

    virtual ~DXAPIError() throw() { }
  };
  
  class DXConnectionError: public DXError {
  public:
    int curl_code; //!< Curl code (returned by libcurl)
    
    DXConnectionError(const std::string msg, int curl_code, const std::string &type=DXErrorTypes::DXCONNECTION_ERROR):
    DXError(msg, type), curl_code(curl_code) {}

    virtual const char* what() const throw() {
      error_msg = type + ": '" + msg + "', Curl error code = '" + boost::lexical_cast<std::string>(curl_code) + "'";
      return (const char*)error_msg.c_str();
    }

    virtual ~DXConnectionError() throw() { }
  };

  //! Represents errors relating to the DXFile class.
  class DXFileError: public DXError {
  public:
    DXFileError(): DXError("Unknown error occured while using DXFile class.") { }
    DXFileError(const std::string &msg, const std::string &type=DXErrorTypes::DXFILE_ERROR): DXError(msg, type) { }
    
    virtual ~DXFileError() throw() { }
  };

  //! Represents errors relating to the DXApp class.
  class DXAppError: public DXError {
  public:
    DXAppError(): DXError("Unknown error occured while using DXApp class.") { }
    DXAppError(const std::string &msg, const std::string &type=DXErrorTypes::DXAPP_ERROR): DXError(msg, type) { }

    virtual ~DXAppError() throw() { }
  };

  //! Represents errors relating to the DXJob class.
  class DXJobError: public DXError {
  public:
    DXJobError(): DXError("Unknown error occured while using DXJob class.") { }
    DXJobError(const std::string &msg, const std::string &type=DXErrorTypes::DXJOB_ERROR): DXError(msg, type) { }
    
    virtual ~DXJobError() throw() { }
  };

  /** Thrown by methods that are not implemented. This can be by design, or
   *  because the methods are under active development. Error message should
   *  provide further detail.
   */
  class DXNotImplementedError: public DXError {
  public:
    DXNotImplementedError(): DXError("Not yet implemented.") { }
    DXNotImplementedError(const std::string &msg, const std::string &type=DXErrorTypes::DXNOT_IMPLEMENTED_ERROR): DXError(msg, type) { }
    
    virtual ~DXNotImplementedError() throw() { }
  };
}

#endif
